package edu.arizona.sista.learn.activity.signature;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import edu.arizona.cs.learn.activity.model.ActivityInstance;
import edu.arizona.cs.learn.activity.model.Bindings;
import edu.arizona.cs.learn.activity.model.Predicate;
import edu.arizona.cs.learn.activity.model.PredicateInterval;
import edu.arizona.cs.learn.activity.model.args.PredAgent;
import edu.arizona.cs.learn.activity.model.args.PredArg;
import edu.arizona.cs.learn.activity.model.args.PredObject;
import edu.arizona.cs.learn.activity.util.PredicateNames;

@Deprecated
public class ChaseSignature implements ActivitySignature {
	
	private static final String SUBJECT = "?x";
	private static final String OBJECT = "?y";

	/**
	 * Detect all occurences of 'SUBJECT chase OBJECT'.
	 */
	@Override
	public List<PredicateInterval> find(ActivityInstance instance) {
		
		// Make new copy of activity
		ActivityInstance inst = instance.copy();
		
		// Infer catch intention
		doInferences(inst);
		
		// Generate chase pattern
		List<Predicate> concurrentPattern = new ArrayList<Predicate>();
		concurrentPattern.add(  new Predicate( PredicateNames.DISTANCE_DECREASING_TO_STATIONARY, new PredAgent(SUBJECT, null), new PredObject(OBJECT, null) )  );
		concurrentPattern.add(  new Predicate( PredicateNames.INTENDS_TO_CATCH, new PredAgent(SUBJECT, null), new PredObject(OBJECT, null) )  );
		concurrentPattern.add(  new Predicate( PredicateNames.DISTANCE_INCREASING_TO_STATIONARY, new PredObject(OBJECT, null), new PredAgent(SUBJECT, null) )  );
		concurrentPattern.add(  new Predicate( PredicateNames.MOVING_FAST_HUMAN, new PredObject(OBJECT, null) )  );
		concurrentPattern.add(  new Predicate( PredicateNames.MOVING_FAST_HUMAN, new PredAgent(SUBJECT, null) )  );
		
		// Detect chase pattern
		PredicateInterval maxInterval = new PredicateInterval(new Predicate("max"), 0, Integer.MAX_VALUE);
		List<PredicateInterval> resultIntervals = new ArrayList<PredicateInterval>();
		List<Bindings> resultBindings = new ArrayList<Bindings>();
		findAllConcurrentPattern(inst.getIntervalsMap(), concurrentPattern, maxInterval, null, resultIntervals, resultBindings);
		
		for (int i = 0; i < resultIntervals.size(); i++) {
			Predicate c = new Predicate(PredicateNames.CHASE, new PredAgent(SUBJECT, null), new PredObject(OBJECT, null));
			c.bindArguments(resultBindings.get(i));
			resultIntervals.get(i).setPredicate(c);
		}
		
		return resultIntervals;
	}
	
	private void findAllConcurrentPattern(Map<String, List<PredicateInterval>> fluentsMap,
			List<Predicate> predicates, PredicateInterval pi, Bindings currentBindings,
			List<PredicateInterval> resultIntervals, List<Bindings> resultBindings) {
		
		if (predicates.isEmpty()) {
			resultBindings.add(currentBindings);
			resultIntervals.add(pi);
			return;
		}
		
		Predicate fluentPredicate = predicates.remove(0);
		
		List<PredicateInterval> intervals = fluentsMap.get(fluentPredicate.getName());
		
		if (intervals != null) {
			for (PredicateInterval interval : intervals) {
				
				Bindings validBinding = fluentPredicate.getValidBinding(interval.getPredicate(), currentBindings);
				List<Predicate> copyPreds = new ArrayList<Predicate>(predicates);
				
				if( validBinding != null ){
					PredicateInterval c = concurrent(interval, pi);
					if (c != null) {
						findAllConcurrentPattern(fluentsMap, copyPreds, c, validBinding, resultIntervals, resultBindings);
					}
				}
			}
			
		}
	}
	
	private ActivityInstance doInferences(ActivityInstance instance){
		
		//If [DDTS(x,y), s, e] infer [ITC(x,y), s, e]
		Predicate ddtsPred = new Predicate(PredicateNames.DISTANCE_DECREASING_TO_STATIONARY, new PredAgent(SUBJECT,null), new PredObject(OBJECT,null));
		Predicate itcPred = new Predicate(PredicateNames.INTENDS_TO_CATCH, new PredAgent(SUBJECT,null), new PredObject(OBJECT,null));
		
		List<PredicateInterval> ddtsIntervals = instance.getIntervals(ddtsPred.getName());
		if (ddtsIntervals != null) {
			for (PredicateInterval pi : ddtsIntervals) {
				// Get binding from ddts
				Bindings bindings = ddtsPred.getValidBinding(pi.getPredicate(), null);
				
				// Assert that the binding is valid (two arguments refer to distinct objects)
				if (bindings != null && bindings.get(ddtsPred.getArg(0)) != null && !bindings.get(ddtsPred.getArg(0)).equals(bindings.get(ddtsPred.getArg(1)))) {
					
					// Create new intention predicate from ddts predicate
					PredicateInterval interval = pi.copy();
					interval.setPredicate( itcPred.copy() );
					interval.getPredicate().bindArguments(bindings);
					
					// Add inferred intention to activity instance
					instance.addInterval(interval);
				}
			}
		}
		
		return instance;
	}
	
	private PredicateInterval concurrent(PredicateInterval a, PredicateInterval b) {
		if (a.end > b.start && a.start < b.end) {
			PredicateInterval c = a.copy();
			c.start = Math.max(a.start, b.start);
			c.end = Math.min(a.end, b.end);
			
			List<Predicate> concurrents = new ArrayList<Predicate>();
			//If either (or both) of these was already an instance of concurrent, let's
			//  concatenate *its* concurrencies with the other
			if( a.getPredicate().getName().equals("concurrent") 
				    || b.getPredicate().getName().equals("concurrent") ) {
				
				if( a.getPredicate().getName().equals("concurrent") ){
					for( PredArg arg : a.getPredicate().getArgs() )
						concurrents.add((Predicate)arg);
				}
				if( b.getPredicate().getName().equals("concurrent") ){
					for( PredArg arg : b.getPredicate().getArgs() )
						concurrents.add((Predicate)arg);
				}
				
			} else {
				//Otherwise we just add the two predicates
				concurrents.add(a.getPredicate());
				concurrents.add(b.getPredicate());
				
			}
			c.setPredicate(new Predicate("concurrent", concurrents));
			
			return c;
		}
		return null;
	}

}
